package com.krt.facenettydemo;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.TextureView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.arcsoft.face.AgeInfo;
import com.arcsoft.face.ErrorInfo;
import com.arcsoft.face.Face3DAngle;
import com.arcsoft.face.FaceEngine;
import com.arcsoft.face.FaceFeature;
import com.arcsoft.face.FaceInfo;
import com.arcsoft.face.FaceSimilar;
import com.arcsoft.face.GenderInfo;
import com.arcsoft.face.util.ImageUtils;
import com.blankj.utilcode.util.ActivityUtils;
import com.blankj.utilcode.util.LogUtils;
import com.blankj.utilcode.util.ToastUtils;
import com.google.gson.Gson;
import com.krt.facenettydemo.camera.CameraHelper;
import com.krt.facenettydemo.camera.CameraListener;
import com.krt.facenettydemo.draw.DrawHelper;
import com.krt.facenettydemo.draw.DrawInfo;
import com.krt.facenettydemo.draw.FaceRectView;
import com.krt.facenettydemo.netty.AesUtil;
import com.krt.facenettydemo.netty.NettyClient;
import com.krt.facenettydemo.utils.Base64Util;
import com.krt.facenettydemo.utils.NV21ToBitmap;
import com.zyao89.view.zloading.ZLoadingDialog;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

import static com.krt.facenettydemo.base.Constants.CachePath;
import static com.krt.facenettydemo.base.Constants.THRESHOLD;
import static com.zyao89.view.zloading.Z_TYPE.DOUBLE_CIRCLE;

/**
 * @author hyj
 * @time 2019/9/10 10:54
 * @class describe
 */
public class FaceVerActivity extends AppCompatActivity {

    private static final String TAG = "FaceVerActivity";

    /**
     * 主图的特征数据
     */
    private FaceFeature mainFeature;

    /**
     * 本地存储的人脸特征
     */
    List<FaceFeature> faceInfoList = new ArrayList<>();

    private FaceEngine faceEngine;
    private int faceEngineCode = -1;
//    private Bitmap mainBitmap;

//    FaceFeature remoteFeature;  //远程协议接收的人脸特性

    private NV21ToBitmap nv21ToBitmap;

    TextureView mCameraView;
    CameraHelper cameraHelper;
    DrawHelper drawHelper;
    ImageView suf;
    FaceRectView faceRect;

//    List<IdentityInfo> idc = new ArrayList<>();

    @SuppressLint("HandlerLeak")
    Handler mHander = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == 1) {
                String path = msg.obj.toString();
                File upd = new File(path);
                if (!upd.exists()) {
                    return;
                }
                Observable.just(upd)
                        .filter((file) -> {
                            Bitmap map = null;
                            FileInputStream in = null;
                            try {
                                in = new FileInputStream(file);
                                map = BitmapFactory.decodeStream(in);
                                in.close();
                                FaceFeature dbF = compareOne(map);
                                if (dbF == null) {
                                    return false;
                                }
                                faceInfoList.add(dbF);
                                return true;
                            } catch (Exception e) {
                                return false;
                            }
                        })
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe();
            } else if (msg.what == 2) {
                Bitmap bitmap = (Bitmap) msg.obj;
                FaceFeature faceFeature = compareOne(bitmap);
                if (faceFeature != null)
                    faceInfoList.add(faceFeature);
            }
        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        setContentView(R.layout.activity_face);
        mCameraView = findViewById(R.id.cameraPreview);
        suf = findViewById(R.id.suf);
        faceRect = findViewById(R.id.faceRect);
        nv21ToBitmap = new NV21ToBitmap(this);
        initEngine();

        /**
         * 测试数据
         */
//        idc.add(new IdentityInfo((long) 0, "黄毅珺", "男", "汉族", "", "", "", "", "13", "", "", "", "", "", "", "", "", ""));
//        idc.add(new IdentityInfo((long) 0, "杨卫军", "男", "汉族", "", "", "", "", "14", "", "", "", "", "", "", "", "", ""));
//        idc.add(new IdentityInfo((long) 0, "黄福运", "男", "汉族", "", "", "", "", "15", "", "", "", "", "", "", "", "", ""));

        initFaceDB();
        UpdService.setUpdFileHandler(mHander);
    }

    private void initEngine() {
        faceEngine = new FaceEngine();
        faceEngineCode = faceEngine.init(this, FaceEngine.ASF_DETECT_MODE_IMAGE, FaceEngine.ASF_OP_0_ONLY,
                16, 6, FaceEngine.ASF_FACE_RECOGNITION | FaceEngine.ASF_AGE | FaceEngine.ASF_FACE_DETECT | FaceEngine.ASF_GENDER | FaceEngine.ASF_FACE3DANGLE);

        Log.i(TAG, "initEngine: init " + faceEngineCode);

        if (faceEngineCode != ErrorInfo.MOK) {
            Toast.makeText(this, "SDK激活失败，错误码:" + faceEngineCode, Toast.LENGTH_SHORT).show();
        }
    }

    private void initCamera() {
        int cameraId = 0;
        //相机是否镜像
        boolean isMirror = true;

        cameraHelper = new CameraHelper.Builder()
                .previewViewSize(new Point(mCameraView.getMeasuredWidth(), mCameraView.getMeasuredHeight()))
                .rotation(ActivityUtils.getTopActivity().getWindowManager().getDefaultDisplay().getRotation())
                .specificCameraId(cameraId)
                .isMirror(isMirror)
                .previewOn(mCameraView)
                .cameraListener(cameraListener)
                .takePictureType(CameraHelper.TakePictureType.NV21)
                .build();
        //相机工具类初始化
        cameraHelper.init();
        cameraHelper.start();
    }

    private void unInitEngine() {
        if (faceEngine != null) {
            faceEngineCode = faceEngine.unInit();
            Log.i(TAG, "unInitEngine: " + faceEngineCode);
        }
    }

    private void unInitCamera() {
        if (cameraHelper != null) {
            cameraHelper.release();
            cameraHelper = null;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        initCamera();
    }

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        UpdService.setUpdFileHandler(null);
        if (mDisposable != null) {
            mDisposable.dispose();
            mDisposable = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unInitEngine();
        unInitCamera();
        EventBus.getDefault().unregister(this);
    }

    public void exit(View view) {
        finish();
    }

    CameraListener cameraListener = new CameraListener() {

        @Override
        public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
            drawHelper = new DrawHelper(mCameraView.getWidth(), mCameraView.getHeight(),
                    faceRect.getWidth(), faceRect.getHeight(), displayOrientation,
                    cameraId, isMirror, false, false);
        }

        @Override
        public void onPreview(byte[] nv21, Camera camera) {
            Camera.Size size = camera.getParameters().getPreviewSize();

            byte[] data = NV21_rotate_to_90(nv21, size.width, size.height);
            faceRect.clearFaceInfo();
            List<FaceInfo> faceInfos = new ArrayList<>();
            int detectCode = faceEngine.detectFaces(data, size.height, size.width, FaceEngine.CP_PAF_NV21, faceInfos);
            if (detectCode == 0) {
                if (faceInfos != null && faceInfos.size() > 0) {
                    Rect rect = faceInfos.get(0).getRect();
                    int w = rect.width() / 2;
                    int l = rect.left;
                    int t = rect.top - w;
                    Rect rect1 = new Rect(l, t, l + w,
                            t + w);
                    DrawInfo drawInfo = new DrawInfo();
                    drawInfo.setColor(Color.YELLOW);
                    drawInfo.setRect(rect1);
                    drawHelper.draw(faceRect, drawInfo);
                }
            }

        }

        @Override
        public void onCameraClosed() {
        }

        @Override
        public void onCameraError(Exception e) {
        }

        @Override
        public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {

        }

        @Override
        public void onTakePicture(int width, int height, CameraHelper.TakePictureType takePictureType, int displayOrientation, byte[] data) {
            addMainFaceEngine(nv21ToBitmap.nv21ToBitmap(data, width, height));
            compare();
        }
    };

    public void addMainFaceEngine(Bitmap bm) {

        bm = rotateBitmap(bm, 90);
        Bitmap bitmap = ImageUtils.alignBitmapForBgr24(bm);
        faceRect.clearFaceInfo();
        if (bitmap == null) {
            return;
        }

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        final byte[] bgr24 = ImageUtils.bitmapToBgr24(bitmap);

        if (bgr24 != null) {

            List<FaceInfo> faceInfos = new ArrayList<>();
            int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfos);
            if (detectCode != 0) {
//                ToastUtils.showShort("识别人脸时发生错误：" + detectCode);
                return;
            }

            if (faceInfos.size() == 0) {
//                ToastUtils.showShort("尚未检测出人脸，请正视屏幕");
                return;
            }

            mainFeature = new FaceFeature();
            int res = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfos.get(0), mainFeature);
            if (res != ErrorInfo.MOK) {
//                ToastUtils.showShort("人脸提取失败，请重试:" + res);
                mainFeature = null;
                return;
            }
        }

//        if (faceInfoList == null || faceInfoList.size() == 0) {
//            ToastUtils.showShort("人脸库尚未提取");
//        }
    }

    private void compare() {

        if (mainFeature == null) {
            return;
        }

//        LogUtils.eTag(TAG, "已采集摄像头人脸，数据人脸" + faceInfoList.size() + "个");

        for (int i = 0; i < faceInfoList.size(); i++) {
            FaceSimilar similar = new FaceSimilar();
            faceEngine.compareFaceFeature(mainFeature, faceInfoList.get(i), similar);

            LogUtils.eTag(TAG, "和数据库人脸相似度为" + similar.getScore());
            if (similar.getScore() >= THRESHOLD) {
                //成功
                ToastUtils.showShort("验证成功");
//                if (!TextUtils.isEmpty(getInfo()) && NettyClient.getInstance().getChannel() != null) {
//                    String info = getInfo();
//                    NettyClient.getInstance().getChannel().writeAndFlush(info);
//                }
                mainFeature = null;
                return;
            }
        }
//        ToastUtils.showShort("未检测到人脸,请重试!");
        mainFeature = null;
    }

    ZLoadingDialog dialog = new ZLoadingDialog(this);

    @SuppressLint("CheckResult")
    private void initFaceDB() {

        dialog.setLoadingBuilder(DOUBLE_CIRCLE)//设置类型
                .setLoadingColor(Color.BLACK)//颜色
                .setHintText("正在加载人脸库...")
                .show();

        File filepath = new File(CachePath);
        if (!filepath.exists()) {
            ToastUtils.showShort("当前未采集照片至人脸库");
        }

        File[] files = filepath.listFiles();

        Observable.fromArray(files)
                .filter((file) -> {
                    Bitmap map = null;
                    FileInputStream in = null;
                    try {
                        in = new FileInputStream(file);
                        map = BitmapFactory.decodeStream(in);
                        in.close();
                    } catch (Exception e) {
                        return false;
                    }

                    FaceFeature dbF = compareOne(map);
                    if (dbF == null) {
                        return false;
                    }
                    faceInfoList.add(dbF);
                    return true;
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<File>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(File identityInfo) {

                    }

                    @Override
                    public void onError(Throwable e) {
                        ToastUtils.showShort("人脸库加载失败");
                    }

                    @Override
                    public void onComplete() {
                        ToastUtils.showShort("人脸库加载完成");
                        dialog.dismiss();
                        startTimerTask();
                    }
                });
    }

    private FaceFeature compareOne(Bitmap bmp) {

        FaceFeature faceFeature = new FaceFeature();

        Bitmap bitmap = ImageUtils.alignBitmapForBgr24(bmp);

        if (bitmap == null) {
            LogUtils.eTag(TAG, "传入图像为空");
            return faceFeature;
        }

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        final byte[] bgr24 = ImageUtils.bitmapToBgr24(bitmap);

        if (bgr24 != null) {
            List<FaceInfo> faceInfos = new ArrayList<>();
            int detectCode = faceEngine.detectFaces(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfos);
            if (detectCode != 0) {
                LogUtils.eTag(TAG, "detectFaces code:" + detectCode);
                return faceFeature;
            }

            if (faceInfos.size() == 0) {
                LogUtils.eTag(TAG, "detectFaces未采集到人脸");
                return faceFeature;
            }

            int r = faceEngine.extractFaceFeature(bgr24, width, height, FaceEngine.CP_PAF_BGR24, faceInfos.get(0), faceFeature);

            LogUtils.eTag(TAG, "extractFaceFeature code:" + r);
        }

        return faceFeature;
    }

    public void startCompare(View view) {
        cameraHelper.takePicture();
    }

    private Bitmap rotateBitmap(Bitmap origin, float alpha) {
        if (origin == null) {
            return null;
        }
        int width = origin.getWidth();
        int height = origin.getHeight();
        Matrix matrix = new Matrix();
        matrix.setRotate(alpha);
        // 围绕原地进行旋转
        Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
        if (newBM.equals(origin)) {
            return newBM;
        }
        origin.recycle();
        return newBM;
    }

    private String getInfo() {
        String json = "";
        try {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("msg", "成功");
            jsonObject.put("code", "0");
            jsonObject.put("data", 1);
            jsonObject.put("timestamp", System.currentTimeMillis());
            json = AesUtil.getAESEncrypt(jsonObject.toString(), "QKhyyuXe37SGjMkT");
            return json;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    private Disposable mDisposable;

    private void startTimerTask() {
        Observable.interval(1, 5, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<Long>() {
                    @Override
                    public void onSubscribe(Disposable d) {
                        mDisposable = d;
                    }

                    @Override
                    public void onNext(Long aLong) {
                        startCompare(null);
                    }

                    @Override
                    public void onError(Throwable e) {
                        if (mDisposable != null)
                            mDisposable.dispose();
                    }

                    @Override
                    public void onComplete() {

                    }
                });
    }

    //********************************************************************************************

    private byte[] NV21_rotate_to_90(byte[] nv21_data, int width, int height) {
        int y_size = width * height;
        int buffser_size = y_size * 3 / 2;
        byte[] nv21_rotated = new byte[buffser_size];

        int i = 0;
        int startPos = (height - 1) * width;
        for (int x = 0; x < width; x++) {
            int offset = startPos;
            for (int y = height - 1; y >= 0; y--) {
                nv21_rotated[i] = nv21_data[offset + x];
                i++;
                offset -= width;
            }
        }

        i = buffser_size - 1;
        for (int x = width - 1; x > 0; x = x - 2) {
            int offset = y_size;
            for (int y = 0; y < height / 2; y++) {
                nv21_rotated[i] = nv21_data[offset + x];
                i--;
                nv21_rotated[i] = nv21_data[offset + (x - 1)];
                i--;
                offset += width;
            }
        }
        return nv21_rotated;
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void event(Object msg) {
        try {
            String json = (String) msg;
            Gson gson = new Gson();
            NettyBean bean = gson.fromJson(json, NettyBean.class);
            byte[] pics = Base64Util.decode(bean.getPic());
            Bitmap bitmap = BitmapFactory.decodeByteArray(pics, 0, pics.length);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


}
